using System;
using System.Threading;
using System.Windows.Controls;
using Microsoft.Practices.CompositeUI;
using NUnit.Framework;
using SCSFContrib.CompositeUI.WPF.Activation;

namespace SCSFContrib.CompositeUI.WPF.UnitTest.Activation
{
	[TestFixture]
	public sealed class FrameworkElementActivationServiceTest : UnitTest
	{
		private TestableRootWorkItem _workItem;
		private FrameworkElementActivationService _frameworkElementActivationService;

		protected override void SetUpCore()
		{
			base.SetUpCore();
			_workItem = new TestableRootWorkItem();
			_frameworkElementActivationService = new FrameworkElementActivationService(_workItem);
		}

		[Test]
		[ExpectedException(typeof(ArgumentNullException))]
		public void Constructor_ShouldThrowIfWorkItemIsNull()
		{
			_frameworkElementActivationService = new FrameworkElementActivationService(null);
		}

		[Test]
		[ExpectedException(typeof(ArgumentNullException))]
		public void FrameworkElementAdded_ShouldThrowIfFrameworkElementIsNull()
		{
			_frameworkElementActivationService.FrameworkElementAdded(null);
		}

		[Test]
		public void FrameworkElementAdded_ShouldntThrowIfFrameworkElementIsNonNull()
		{
			_frameworkElementActivationService.FrameworkElementAdded(new Button());
		}

		[Test]
		public void FrameworkElementAdded_ShouldWorkFromAnyThread()
		{
			Button button = null;

			Thread thread = new Thread(delegate()
			{
				button = new Button();
			});

			thread.SetApartmentState(ApartmentState.STA);
			thread.Start();
			thread.Join();

			Assert.IsNotNull(button);
			_frameworkElementActivationService.FrameworkElementAdded(button);
		}

		[Test]
		[ExpectedException(typeof(ArgumentNullException))]
		public void FrameworkElementRemoved_ShouldThrowIfFrameworkElementIsNull()
		{
			_frameworkElementActivationService.FrameworkElementRemoved(null);
		}

		[Test]
		public void FrameworkElementRemoved_ShouldntThrowIfFrameworkElementIsNonNull()
		{
			_frameworkElementActivationService.FrameworkElementRemoved(new Button());
		}

		[Test]
		public void FrameworkElementRemoved_ShouldWorkFromAnyThread()
		{
			Button button = null;

			Thread thread = new Thread(delegate()
			{
				button = new Button();
			});

			thread.SetApartmentState(ApartmentState.STA);
			thread.Start();
			thread.Join();

			Assert.IsNotNull(button);
			_frameworkElementActivationService.FrameworkElementRemoved(button);
		}

		[Test]
		[ExpectedException(typeof(ArgumentNullException))]
		public void GetIsExempt_ShouldThrowIfFrameworkElementIsNull()
		{
			FrameworkElementActivationService.GetIsExempt(null);
		}

		[Test]
		public void GetIsExempt_ShouldDefaultToFalse()
		{
			Assert.IsFalse(FrameworkElementActivationService.GetIsExempt(new TextBox()));
			Assert.IsFalse(FrameworkElementActivationService.GetIsExempt(new TextBlock()));
			Assert.IsFalse(FrameworkElementActivationService.GetIsExempt(new Button()));
		}

		[Test]
		public void GetIsExempt_ShouldDefaultToTrueForMenuItems()
		{
			Assert.IsTrue(FrameworkElementActivationService.GetIsExempt(new MenuItem()));
		}

		[Test]
		[ExpectedException(typeof(ArgumentNullException))]
		public void SetIsExempt_ShouldThrowIfFrameworkElementIsNull()
		{
			FrameworkElementActivationService.SetIsExempt(null, false);
		}

		[Test]
		public void SetIsExempt_ShouldAssignGivenValue()
		{
			Button button = new Button();
			TextBox textBox = new TextBox();
			MenuItem menuItem1 = new MenuItem();
			MenuItem menuItem2 = new MenuItem();

			FrameworkElementActivationService.SetIsExempt(button, true);
			FrameworkElementActivationService.SetIsExempt(textBox, false);
			FrameworkElementActivationService.SetIsExempt(menuItem1, false);
			FrameworkElementActivationService.SetIsExempt(menuItem2, true);

			Assert.IsTrue(FrameworkElementActivationService.GetIsExempt(button));
			Assert.IsFalse(FrameworkElementActivationService.GetIsExempt(textBox));
			Assert.IsFalse(FrameworkElementActivationService.GetIsExempt(menuItem1));
			Assert.IsTrue(FrameworkElementActivationService.GetIsExempt(menuItem2));
		}

		[Test]
		public void ActivateWorkItemForFrameworkElement_ShouldActivateWorkItem()
		{
			Assert.AreEqual(WorkItemStatus.Inactive, _workItem.Status);
			Assert.IsTrue(InvokePrivateMethod<bool>(_frameworkElementActivationService, "ActivateWorkItemForFrameworkElement", new Button()));
			Assert.AreEqual(WorkItemStatus.Active, _workItem.Status);
		}

		[Test]
		public void ActivateWorkItemForFrameworkElement_ShouldNotActivateWorkItemIfFrameworkElementIsExempt()
		{
			Button button = new Button();
			FrameworkElementActivationService.SetIsExempt(button, true);

			Assert.AreEqual(WorkItemStatus.Inactive, _workItem.Status);
			Assert.IsFalse(InvokePrivateMethod<bool>(_frameworkElementActivationService, "ActivateWorkItemForFrameworkElement", new MenuItem()));
			Assert.IsFalse(InvokePrivateMethod<bool>(_frameworkElementActivationService, "ActivateWorkItemForFrameworkElement", button));
			Assert.AreEqual(WorkItemStatus.Inactive, _workItem.Status);
		}
	}
}
